﻿using System;
using System.Globalization;
using System.Linq;

namespace Ruction
{
	/// <summary>Handles the generation of random names.</summary>
	internal static class NameGenerator
	{
		#region Member Variables

		/// <summary>Private storage for letter proportions.</summary>
		private static int[] _letterPrp;

		/// <summary>Private storage for letter pecrentages.</summary>
		private static float[] _letterPct;

		/// <summary>Private storage for name 'spice'.</summary>
		private static string[] _nameSpice;

		/// <summary>Private randomizer.</summary>
		private static Random _rnd;

		#endregion Member Variables

		#region Constructors

		/// <summary>Static constructor for name generation data.</summary>
		static NameGenerator()
		{
			_rnd = new Random(DateTime.Now.Millisecond);
			_nameSpice = new string[26];
			_letterPct = new float[26];
			_letterPrp = new int[26];

			InitNameGen();
		}

		#endregion Constructors

		#region Properties

		#region NameSpice
		/// <summary>Gets the name spice string array.</summary>
		public static string[] NameSpice { get { return _nameSpice; } }
		#endregion NameSpice

		#endregion Properties

		#region Methods

		#region GenerateName
		/// <summary>Generates a random name.</summary>
		/// <param name="min">The minimum name length.</param>
		/// <param name="max">The maximum name length.</param>
		/// <param name="specials">True to use special strings in the name.</param>
		/// <returns>A string containing a randomly generated name.</returns>
		internal static string GenerateName(int min, int max, bool specials)
		{
			int i = 0;
			bool GoodWord = false;
			string retVal = string.Empty;
			char LastChar = char.MinValue, TwoCharsAgo = char.MinValue;

			do
			{
				retVal = Convert.ToChar(Convert.ToInt32(_rnd.NextDouble() * 26) + 65).ToString();
				LastChar = retVal[0];
				GoodWord = true;

				for (i = 0; i < (Convert.ToInt32(_rnd.NextDouble() * (max - min + 1)) + min) - 1; i++)
				{
					if (Consonant(LastChar))
					{
						if (Consonant(TwoCharsAgo))
						{
							if (LastChar.Equals('y') || LastChar.Equals('Y'))
							{
								retVal += AddChars("BCDFGKLMNPRSTVXZ");
							}
							else { retVal += AddChars("AEIOU"); }
						}
						else
						{
							if (_rnd.NextDouble() < 0.2)
							{
								if (LastChar.Equals('q') || LastChar.Equals('Q'))
								{
									retVal += AddChars("U");
								}
								else { retVal += AddChars("AEIOU"); }
							}
							else
							{
								#region BIG Switch Statement
								switch (LastChar)
								{
									case 'B':
									case 'b':
										retVal += AddChars("BLRSY");
										break;
									case 'C':
									case 'c':
										retVal += AddChars("CHKLRSTY");
										break;
									case 'D':
									case 'd':
										retVal += AddChars("DRSWY");
										break;
									case 'F':
									case 'f':
										retVal += AddChars("FLRSTY");
										break;
									case 'G':
									case 'g':
										retVal += AddChars("GHLRS");
										break;
									case 'H':
									case 'h':
										retVal += AddChars("AEIOUY");
										break;
									case 'J':
									case 'j':
										retVal += AddChars("AEIOU");
										break;
									case 'K':
									case 'k':
										retVal += AddChars("HKLNRSTW");
										break;
									case 'L':
									case 'l':
										retVal += AddChars("CDKLMNPSTY");
										break;
									case 'M':
									case 'm':
										retVal += AddChars("MPSY");
										break;
									case 'N':
									case 'n':
										retVal += AddChars("CDKNSTY");
										break;
									case 'P':
									case 'p':
										retVal += AddChars("HLPRSTY");
										break;
									case 'Q':
									case 'q':
										retVal += AddChars("U");
										break;
									case 'R':
									case 'r':
										retVal += AddChars("CDKMNPRSTY");
										break;
									case 'S':
									case 's':
										retVal += AddChars("CHKLMNPSTWY");
										break;
									case 'T':
									case 't':
										retVal += AddChars("HRSTWY");
										break;
									case 'V':
									case 'v':
										retVal += AddChars("LRS");
										break;
									case 'W':
									case 'w':
										retVal += AddChars("HKST");
										break;
									case 'X':
									case 'x':
										retVal += AddChars("CSYAEIOU");
										break;
									case 'Y':
									case 'y':
										retVal += AddChars("LSTKMNPC");
										break;
									case 'Z':
									case 'z':
										retVal += AddChars("HZZZ");
										break;
								}
								#endregion BIG Switch Statement
							}
						}
					}
					else
					{
						if (Consonant(TwoCharsAgo))
						{
							if (_rnd.NextDouble() < 0.9 && retVal.Length >= 2 && !retVal.Substring(retVal.Length - 2, 2).Equals("QU", StringComparison.OrdinalIgnoreCase))
							{
								retVal += AddChars("BCDFGHJKLMNPQRSTVWX");
							}
							else
							{
								switch (LastChar)
								{
									case 'A':
									case 'a':
										retVal += AddChars("IU");
										break;
									case 'E':
									case 'e':
										retVal += AddChars("AEIOU");
										break;
									case 'I':
									case 'i':
										retVal += AddChars("AEO");
										break;
									case 'O':
									case 'o':
										retVal += AddChars("AEIOU");
										break;
									case 'U':
									case 'u':
										retVal += AddChars("AEI");
										break;
								}
							}
						}
						else
						{
							retVal += AddChars("BCDFGKLMNPRST");
						}
					}

					TwoCharsAgo = LastChar;
					LastChar = retVal[retVal.Length - 1];
				}

				TwoCharsAgo = retVal[0];
				LastChar = retVal.Length > 1 ? retVal[1] : TwoCharsAgo;

				if (TwoCharsAgo.Equals(LastChar)) GoodWord = false;

				if ((LastChar.Equals('s') || LastChar.Equals('S')) || (LastChar.Equals('d') || LastChar.Equals('D')))
				{
					if (Consonant(TwoCharsAgo)) { GoodWord = false; }
				}

				char[] checkChars = new char[] { 'T', 't', 'K', 'k', 'M', 'm', 'N', 'n', 'P', 'p', 'C', 'c' };
				if (checkChars.Contains(LastChar))
				{
					if (Consonant(TwoCharsAgo) && (!TwoCharsAgo.Equals('S') && !TwoCharsAgo.Equals('s')))
						GoodWord = false;
				}

				if ((TwoCharsAgo.Equals('Y') || TwoCharsAgo.Equals('y')) && Consonant(LastChar)) GoodWord = false;

				if (retVal.Length >= 2 && !retVal.Substring(retVal.Length - 2, 2).Equals("QU", StringComparison.OrdinalIgnoreCase))
					GoodWord = false;

				if (retVal.Contains("IY") || retVal.Contains("WI") ||
					retVal.Contains("YI") || retVal.Contains("UY") ||
					retVal.Contains("UW") || retVal.Contains("IH") ||
					retVal.Contains("UH")) GoodWord = false;

				if ((TwoCharsAgo.Equals('X') || TwoCharsAgo.Equals('x')) || (retVal.Length >= 2 && retVal.Substring(0, 2).Equals("IL", StringComparison.OrdinalIgnoreCase)))
					GoodWord = false;
			}
			while (!GoodWord);

			LastChar = retVal[retVal.Length - 1];
			TwoCharsAgo = retVal[retVal.Length - 2];

			if ((Consonant(LastChar) && Consonant(TwoCharsAgo)) ||
				retVal.Length < 4 || _rnd.NextDouble() < 0.15)
			{
				if (Consonant(LastChar) && (!LastChar.Equals('Y') || !LastChar.Equals('y')))
				{
					retVal += _nameSpice[(Convert.ToInt32(_rnd.NextDouble() * 6) + 17)];
				}
				else { retVal += _nameSpice[(Convert.ToInt32(_rnd.NextDouble() * 6) + 11)]; }
			}

			if (specials)
			{
				if (_rnd.NextDouble() < 0.13)
				{
					retVal = _nameSpice[(Convert.ToInt32(_rnd.NextDouble() * 10) + 1)] + retVal;
				}
			}

			retVal = CultureInfo.CurrentCulture.TextInfo.ToTitleCase(retVal.ToLower());

			return retVal;
		}
		#endregion GenerateName

		#region AddChars
		/// <summary>Selects a random character from a set of characters.</summary>
		/// <param name="letters">The characters to choose from.</param>
		/// <returns>A string containing a single character chosen at random.</returns>
		private static string AddChars(string letters)
		{
			int j = 0;
			string retVal = string.Empty;
			float letterTotal = 0, letterTresh = 0, randLetter = 0;

			for (j = 0; j < letters.Length; j++)
			{
				letterTotal += _letterPrp[Convert.ToInt32(letters[j]) - 65];
			}

			letterTresh = 0;
			for (j = 0; j < letters.Length; j++)
			{
				letterTresh += (_letterPrp[Convert.ToInt32(letters[j]) - 65] / letterTotal);
				_letterPct[j] = letterTresh;
			}

			randLetter = Convert.ToSingle(_rnd.NextDouble());
			for (j = 0; j < letters.Length; j++)
			{
				if (randLetter <= _letterPct[j])
				{
					retVal = letters[j].ToString();
					break;
				}
			}

			return retVal;
		}
		#endregion AddChars

		#region Consonant
		/// <summary>Determines if a given character is a consonant or not.</summary>
		/// <param name="c">The character to analyze.</param>
		/// <returns>True if the character is an english language consonant, otherwise false.</returns>
		private static bool Consonant(char c)
		{
			bool retVal = false;
			if (!c.Equals(char.MinValue))
			{
				retVal = !(c.Equals('a') || c.Equals('A') ||
								c.Equals('e') || c.Equals('E') ||
								c.Equals('i') || c.Equals('I') ||
								c.Equals('o') || c.Equals('O') ||
								c.Equals('u') || c.Equals('U'));
			}
			return retVal;
		}
		#endregion Consonant

		#region InitNameGen
		/// <summary>Initializes require name generation arrays.</summary>
		private static void InitNameGen()
		{
			_letterPrp[0] = 9;
			_letterPrp[1] = 2;
			_letterPrp[2] = 2;
			_letterPrp[3] = 4;
			_letterPrp[4] = 12;
			_letterPrp[5] = 2;
			_letterPrp[6] = 3;
			_letterPrp[7] = 2;
			_letterPrp[8] = 9;
			_letterPrp[9] = 1;
			_letterPrp[10] = 1;
			_letterPrp[11] = 4;
			_letterPrp[12] = 2;
			_letterPrp[13] = 6;
			_letterPrp[14] = 8;
			_letterPrp[15] = 2;
			_letterPrp[16] = 1;
			_letterPrp[17] = 6;
			_letterPrp[18] = 4;
			_letterPrp[19] = 6;
			_letterPrp[20] = 4;
			_letterPrp[21] = 2;
			_letterPrp[22] = 2;
			_letterPrp[23] = 1;
			_letterPrp[24] = 2;
			_letterPrp[25] = 1;

			_nameSpice[0] = "Upper";
			_nameSpice[1] = "Lower";
			_nameSpice[2] = "Old";
			_nameSpice[3] = "New";
			_nameSpice[4] = "San";
			_nameSpice[5] = "Costa";
			_nameSpice[6] = "North";
			_nameSpice[7] = "South";
			_nameSpice[8] = "East";
			_nameSpice[9] = "West";
			_nameSpice[10] = "tia";
			_nameSpice[11] = "lia";
			_nameSpice[12] = "way";
			_nameSpice[13] = "land";
			_nameSpice[14] = "ton";
			_nameSpice[15] = "burg";
			_nameSpice[16] = "ary";
			_nameSpice[17] = "age";
			_nameSpice[18] = "ia";
			_nameSpice[19] = "any";
			_nameSpice[20] = "ica";
			_nameSpice[21] = "ania";
			_nameSpice[22] = "Isle";
			_nameSpice[23] = "Island";
			_nameSpice[24] = "The Isle of";
		}
		#endregion InitNameGen

		#endregion Methods
	}
}
